package net.techwatch.poi;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@SuppressWarnings("unchecked")
public class PrivateMatcher<T> implements Matcher<T> {

	private Binder binder;
	
	public void setBinder(Binder binder) {
		this.binder = binder;
	}
	
	public T match(T o, String property, Object value) {
		try {
		return methodExecutor(o, property, value);
		} catch(ClassCastException e) {
			return o;
		}
	}
	
	private T methodExecutor(T o, String property, Object value) {
		Method method = binder.getMethod(property);
		
		try {
			 if(binder.getParameterType(property).equals(Date.class)) {
				return (T) method.invoke(o, castToDate(property, value));
			} else if(binder.getParameterType(property).equals(String.class)) {
				return (T) method.invoke(o, value);
			} else if(binder.getParameterType(property).isPrimitive()) {
				return (T) method.invoke(o, castToNumber(property, value));
			}
			
			throw new IllegalArgumentException("Type not supported");
		} catch (IllegalArgumentException e) {
			throw e;
		} catch (IllegalAccessException e) {
			throw new IllegalArgumentException(e);
		} catch (InvocationTargetException e) {
			throw new IllegalArgumentException(e);
		}
	}
	
	private Date castToDate(String property, Object value) {
		String strValue = null;
		if(value instanceof String) {
			strValue = (String) value;
		} else {
			strValue = ((Number) value).toString();
		}
		SimpleDateFormat format = binder.getDateFormat(property);
		if(format == null)
			throw new IllegalArgumentException("No format available");
		
		try {
			return format.parse(strValue);
		} catch(ParseException e) {
			throw new IllegalArgumentException(e);
		}
	}
	
	private Number castToNumber(String property, Object value) {
		Number number = (Number) value;
		try {
			Method convertionMethod = number.getClass().getMethod(binder.getParameterType(property) + "Value");
			return (Number) convertionMethod.invoke(number);
		} catch (SecurityException e) {
			throw new IllegalArgumentException(e);
		} catch (NoSuchMethodException e) {
			throw new IllegalArgumentException(e);
		} catch (IllegalArgumentException e) {
			throw new IllegalArgumentException(e);
		} catch (IllegalAccessException e) {
			throw new IllegalArgumentException(e);
		} catch (InvocationTargetException e) {
			throw new IllegalArgumentException(e);
		}
	}
	

	/* (non-Javadoc)
	 * @see poi.Matcher#matchableProperties()
	 */
	public String[] matchableProperties() {
		return binder.bindableProperties();
	}

}
